#pragma once    // --> 相当于ifndef 避免重复包含头文件

#include <cassert>
#include <cstdint>
#include <atomic>
#include <memory>
#include <mutex>
#include <iostream>

namespace memoryPool
{
    #define MEMORY_POOL_NUM 64
    #define SLOT_BASE_SIZE 8
    #define MAX_SLOT_SIZE 512

    // 修改Slot结构，使用原子指针
    struct Slot
    {
        std::atomic<Slot*> next;
    };

    class MemoryPool
    {
        public:
            // size_t 的作用 规避负值 ,并于标准库malloc，sizeof返回值对齐
            MemoryPool(size_t BlockSize =4096);
            ~MemoryPool();

            void init(size_t);
            void * allocate();
            void deallocate(void*);
        
        private:
            void allocateNewBlock();
            size_t padPointer(char *p,size_t align);
            // 使用CAS操作进行无锁入队和出队
            bool pushFreeList(Slot* Slot);
            Slot * popFreeList();
        private:
            int BlockSize_;  //内存块大小
            int SlotSize_;  //槽大小
            Slot* firstBlock_; //向内存池管理的首个实际内存块指
            Slot* curSlot_; //指向对当前未被使用过的槽
            std::atomic<Slot*> freeList_; //指向空闲的槽（或者是之前释放的槽）
            Slot* lastSlot_; // 作为当前内存块中最后能够存放元素的位置标识（超过该位置需申请新的内存块）
            std::mutex mutexForBlock_; //保证多线程情况下避免不必要的重复开辟内存导致的浪费行为
    };

    class HashBucket
    {
        public:
            static void initMemoryPool();
            static MemoryPool& getMemoryPoll(int index);

            static void* useMemory(size_t size)
            {
                if(size<=0) return nullptr;
                if(size > MAX_SLOT_SIZE) //大于512则需申请新的空间
                    return operator new(size);
                // 相当于size/8向上取整 找到对应标号的Memory Pool 例如：申请10 (10+7)/8=2 2-1=1 1-----16bmemorypool
                return getMemoryPoll(((size+7)/SLOT_BASE_SIZE)-1).allocate();
            }

            static void freeMemory(void * ptr,size_t size)
            {
                //释放空指针是无效操作，直接返回可以避免程序崩溃
                if(!ptr)
                    return;
                //大内存直接调用C++ 底层的内存释放函数进行删除，无需访问内存池
                if(size>MAX_SLOT_SIZE)
                {
                    operator delete(ptr);
                    return;
                }
                // 找到标记进行内存释放
                getMemoryPoll(((size+7)/SLOT_BASE_SIZE)-1).deallocate(ptr);
            }

            template<typename T,typename... Args>
            friend T* newElement(Args&&... args);

            template<typename T>
            friend void deleteElement(T *p);
    };

    template<typename T,typename... Args>
    T* newElement(Args&&... args)
    {
        T* p =nullptr;
        //根据元素大小选取合适的内存池分配内存
        if((p = reinterpret_cast<T*>(HashBucket::useMemory(sizeof(T))))!=nullptr)
        //在分配的内存上构造函数
            //在用户指定的内存位置上（这个内存是已经预先分配好的）构建新的对象
            new(p) T(std::forward<Args>(args)...);
        
        return p;
    }

    template<typename T>
    void deleteElement(T* p)
    {
        if(p)
        {
            p->~T();
            //内存回收
            HashBucket::freeMemory(reinterpret_cast<void*>(p),sizeof(T));
        }
    }

}